package com.ruoyi.system.service.impl

import com.ruoyi.common.constant.Constants
import com.ruoyi.common.constant.UserConstants
import com.ruoyi.common.core.domain.TreeSelect
import com.ruoyi.common.core.domain.entity.SysMenu
import com.ruoyi.common.core.domain.entity.SysUser
import com.ruoyi.common.utils.MyStringUtils
import com.ruoyi.common.utils.SecurityUtils.isAdmin
import com.ruoyi.system.domain.vo.MetaVo
import com.ruoyi.system.domain.vo.RouterVo
import com.ruoyi.system.mapper.SysMenuMapper
import com.ruoyi.system.mapper.SysRoleMapper
import com.ruoyi.system.mapper.SysRoleMenuMapper
import com.ruoyi.system.service.ISysMenuService
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.stereotype.Service
import java.util.*

/**
 * 菜单 业务层处理
 *
 * @author ruoyi
 */
@Service
open class SysMenuServiceImpl : ISysMenuService {
    @Autowired
    private lateinit var menuMapper: SysMenuMapper

    @Autowired
    private lateinit var roleMapper: SysRoleMapper

    @Autowired
    private lateinit var roleMenuMapper: SysRoleMenuMapper

    /**
     * 根据用户查询系统菜单列表
     *
     * @param userId 用户ID
     * @return 菜单列表
     */
    override fun selectMenuList(userId: Long?): List<SysMenu> {
        return selectMenuList(SysMenu(), userId)
    }

    /**
     * 查询系统菜单列表
     *
     * @param menu 菜单信息
     * @return 菜单列表
     */
    override fun selectMenuList(menu: SysMenu, userId: Long?): List<SysMenu> {
        // 管理员显示所有菜单信息
        val menuList: List<SysMenu> = if (SysUser.isAdmin(userId)) {
            menuMapper.selectMenuList(menu)
        } else {
            userId?.let { menu.params?.put("userId", it) }
            menuMapper.selectMenuListByUserId(menu)
        }
        return menuList
    }

    /**
     * 根据用户ID查询权限
     *
     * @param userId 用户ID
     * @return 权限列表
     */
    override fun selectMenuPermsByUserId(userId: Long?): Set<String> {
        val perms = menuMapper.selectMenuPermsByUserId(userId)
        val permsSet = mutableSetOf<String>()
        for (perm in perms) {
            if (MyStringUtils.isNotEmpty(perm)) {
                permsSet.addAll(mutableListOf(*perm.trim { it <= ' ' }.split(",".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()))
            }
        }
        return permsSet
    }

    /**
     * 根据角色ID查询权限
     *
     * @param roleId 角色ID
     * @return 权限列表
     */
    override fun selectMenuPermsByRoleId(roleId: Long?): Set<String> {
        val perms = menuMapper.selectMenuPermsByRoleId(roleId)
        val permsSet: MutableSet<String> = mutableSetOf()
        for (perm in perms) {
            if (MyStringUtils.isNotEmpty(perm)) {
                permsSet.addAll(mutableListOf(*perm.trim { it <= ' ' }.split(",".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()))
            }
        }
        return permsSet
    }

    /**
     * 根据用户ID查询菜单
     *
     * @param userId 用户名称
     * @return 菜单列表
     */
    override fun selectMenuTreeByUserId(userId: Long?): List<SysMenu> {
        val menus: List<SysMenu>  = if (isAdmin(userId)) {
            menuMapper.selectMenuTreeAll()
        } else {
            menuMapper.selectMenuTreeByUserId(userId)
        }
        return getChildPerms(menus, 0)
    }

    /**
     * 根据角色ID查询菜单树信息
     *
     * @param roleId 角色ID
     * @return 选中菜单列表
     */
    override fun selectMenuListByRoleId(roleId: Long?): List<Long> {
        val role = roleMapper.selectRoleById(roleId)
        return menuMapper.selectMenuListByRoleId(roleId, role!!.isMenuCheckStrictly)
    }

    /**
     * 构建前端路由所需要的菜单
     *
     * @param menus 菜单列表
     * @return 路由列表
     */
    override fun buildMenus(menus: List<SysMenu>): List<RouterVo> {
        val routers: MutableList<RouterVo> = LinkedList()
        for (menu in menus) {
            val router = RouterVo()
            router.hidden = "1" == menu.visible
            router.name = getRouteName(menu)
            router.path = getRouterPath(menu)
            router.component = getComponent(menu)
            router.query = menu.query
            router.meta = MetaVo(menu.menuName, menu.icon, "1"==menu.isCache, menu.path)
            val cMenus = menu.children
            if (MyStringUtils.isNotEmpty(cMenus) && UserConstants.TYPE_DIR == menu.menuType) {
                router.alwaysShow = true
                router.redirect = "noRedirect"
                router.children = buildMenus(cMenus)
            } else if (isMenuFrame(menu)) {
                router.meta = null
                val childrenList: MutableList<RouterVo> = mutableListOf()
                val children = RouterVo()
                children.path = menu.path
                children.component = menu.component
                children.name = org.apache.commons.lang3.StringUtils.capitalize(menu.path)
                children.meta = MetaVo(menu.menuName, menu.icon, "1"== menu.isCache, menu.path)
                children.query = menu.query
                childrenList.add(children)
                router.children = childrenList
            } else if (menu.parentId?.toInt() == 0 && isInnerLink(menu)) {
                router.meta = MetaVo(menu.menuName, menu.icon)
                router.path = "/"
                val childrenList: MutableList<RouterVo> = mutableListOf()
                val children = RouterVo()
                val routerPath = innerLinkReplaceEach(menu.path)
                children.path = routerPath
                children.component = UserConstants.INNER_LINK
                children.name = org.apache.commons.lang3.StringUtils.capitalize(routerPath)
                children.meta = MetaVo(menu.menuName, menu.icon, menu.path)
                childrenList.add(children)
                router.children = childrenList
            }
            routers.add(router)
        }
        return routers
    }

    /**
     * 构建前端所需要树结构
     *
     * @param menus 菜单列表
     * @return 树结构列表
     */
    override fun buildMenuTree(menus: MutableList<SysMenu>): List<SysMenu> {
        var returnList: MutableList<SysMenu> = mutableListOf()
        val tempList = menus.map { obj: SysMenu -> obj.menuId }.toMutableList()
        val iterator: Iterator<SysMenu> = menus.iterator()
        while (iterator.hasNext()) {
            val menu = iterator.next()
            // 如果是顶级节点, 遍历该父节点的所有子节点
            if (!tempList.contains(menu.parentId)) {
                recursionFn(menus, menu)
                returnList.add(menu)
            }
        }
        if (returnList.isEmpty()) {
            returnList = menus
        }
        return returnList
    }

    /**
     * 构建前端所需要下拉树结构
     *
     * @param menus 菜单列表
     * @return 下拉树结构列表
     */
    override fun buildMenuTreeSelect(menus: MutableList<SysMenu>): List<TreeSelect> {
        val menuTrees = buildMenuTree(menus)
        return menuTrees.map { menu: SysMenu? -> TreeSelect(menu) }.toMutableList()
    }

    /**
     * 根据菜单ID查询信息
     *
     * @param menuId 菜单ID
     * @return 菜单信息
     */
    override fun selectMenuById(menuId: Long?): SysMenu? {
        return menuMapper.selectMenuById(menuId)
    }

    /**
     * 是否存在菜单子节点
     *
     * @param menuId 菜单ID
     * @return 结果
     */
    override fun hasChildByMenuId(menuId: Long?): Boolean {
        val result = menuMapper.hasChildByMenuId(menuId)
        return result > 0
    }

    /**
     * 查询菜单使用数量
     *
     * @param menuId 菜单ID
     * @return 结果
     */
    override fun checkMenuExistRole(menuId: Long?): Boolean {
        val result = roleMenuMapper.checkMenuExistRole(menuId)
        return result > 0
    }

    /**
     * 新增保存菜单信息
     *
     * @param menu 菜单信息
     * @return 结果
     */
    override fun insertMenu(menu: SysMenu?): Int {
        return menuMapper.insertMenu(menu)
    }

    /**
     * 修改保存菜单信息
     *
     * @param menu 菜单信息
     * @return 结果
     */
    override fun updateMenu(menu: SysMenu?): Int {
        return menuMapper.updateMenu(menu)
    }

    /**
     * 删除菜单管理信息
     *
     * @param menuId 菜单ID
     * @return 结果
     */
    override fun deleteMenuById(menuId: Long?): Int {
        return menuMapper.deleteMenuById(menuId)
    }

    /**
     * 校验菜单名称是否唯一
     *
     * @param menu 菜单信息
     * @return 结果
     */
    override fun checkMenuNameUnique(menu: SysMenu): Boolean {
        val menuId = if (MyStringUtils.isNull(menu.menuId)) -1L else menu.menuId
        val info = menuMapper.checkMenuNameUnique(menu.menuName, menu.parentId)
        return if (MyStringUtils.isNotNull(info) && info!!.menuId != menuId) {
            UserConstants.NOT_UNIQUE
        } else UserConstants.UNIQUE
    }

    /**
     * 获取路由名称
     *
     * @param menu 菜单信息
     * @return 路由名称
     */
    fun getRouteName(menu: SysMenu?): String {
        var routerName: String = org.apache.commons.lang3.StringUtils.capitalize(menu!!.path)
        // 非外链并且是一级目录（类型为目录）
        if (isMenuFrame(menu)) {
            routerName = org.apache.commons.lang3.StringUtils.EMPTY
        }
        return routerName
    }

    /**
     * 获取路由地址
     *
     * @param menu 菜单信息
     * @return 路由地址
     */
    fun getRouterPath(menu: SysMenu?): String? {
        var routerPath = menu!!.path
        // 内链打开外网方式
        if (menu.parentId?.toInt() != 0 && isInnerLink(menu)) {
            routerPath = innerLinkReplaceEach(routerPath)
        }
        // 非外链并且是一级目录（类型为目录）
        if (0 == menu.parentId?.toInt() && UserConstants.TYPE_DIR == menu.menuType && UserConstants.NO_FRAME == menu.isFrame) {
            routerPath = "/" + menu.path
        } else if (isMenuFrame(menu)) {
            routerPath = "/"
        }
        return routerPath
    }

    /**
     * 获取组件信息
     *
     * @param menu 菜单信息
     * @return 组件信息
     */
    private fun getComponent(menu: SysMenu?): String {
        var component = UserConstants.LAYOUT
        if (MyStringUtils.isNotEmpty(menu!!.component) && !isMenuFrame(menu)) {
            component = menu.component!!
        } else if (MyStringUtils.isEmpty(menu.component) && menu.parentId?.toInt() != 0 && isInnerLink(menu)) {
            component = UserConstants.INNER_LINK
        } else if (MyStringUtils.isEmpty(menu.component) && isParentView(menu)) {
            component = UserConstants.PARENT_VIEW
        }
        return component
    }

    /**
     * 是否为菜单内部跳转
     *
     * @param menu 菜单信息
     * @return 结果
     */
    private fun isMenuFrame(menu: SysMenu?): Boolean {
        return menu!!.parentId?.toInt() == 0 && UserConstants.TYPE_MENU == menu.menuType && menu.isFrame == UserConstants.NO_FRAME
    }

    /**
     * 是否为内链组件
     *
     * @param menu 菜单信息
     * @return 结果
     */
    private fun isInnerLink(menu: SysMenu?): Boolean {
        return menu!!.isFrame == UserConstants.NO_FRAME && MyStringUtils.ishttp(menu.path)
    }

    /**
     * 是否为parent_view组件
     *
     * @param menu 菜单信息
     * @return 结果
     */
    private fun isParentView(menu: SysMenu?): Boolean {
        return menu!!.parentId?.toInt() != 0 && UserConstants.TYPE_DIR == menu.menuType
    }

    /**
     * 根据父节点的ID获取所有子节点
     *
     * @param list 分类表
     * @param parentId 传入的父节点ID
     * @return String
     */
    private fun getChildPerms(list: List<SysMenu>?, parentId: Int): List<SysMenu> {
        val returnList: MutableList<SysMenu> = mutableListOf()
        val iterator = list!!.iterator()
        while (iterator.hasNext()) {
            val t = iterator.next()
            // 一、根据传入的某个父节点ID,遍历该父节点的所有子节点
            if (t.parentId == parentId.toLong()) {
                recursionFn(list, t)
                returnList.add(t)
            }
        }
        return returnList
    }

    /**
     * 递归列表
     *
     * @param list 分类表
     * @param t 子节点
     */
    private fun recursionFn(list: List<SysMenu>?, t: SysMenu?) {
        // 得到子节点列表
        val childList = getChildList(list, t)
        t!!.children = childList
        for (tChild in childList) {
            if (hasChild(list, tChild)) {
                recursionFn(list, tChild)
            }
        }
    }

    /**
     * 得到子节点列表
     */
    private fun getChildList(list: List<SysMenu?>?, t: SysMenu?): List<SysMenu> {
        val tlist: MutableList<SysMenu> = mutableListOf()
        val it = list!!.iterator()
        while (it.hasNext()) {
            val n = it.next()
            if (n!!.parentId == t!!.menuId) {
                tlist.add(n)
            }
        }
        return tlist
    }

    /**
     * 判断是否有子节点
     */
    private fun hasChild(list: List<SysMenu?>?, t: SysMenu?): Boolean {
        return getChildList(list, t).isNotEmpty()
    }

    /**
     * 内链域名特殊字符替换
     *
     * @return 替换后的内链域名
     */
    fun innerLinkReplaceEach(path: String?): String {
        return org.apache.commons.lang3.StringUtils.replaceEach(path, arrayOf(Constants.HTTP, Constants.HTTPS, Constants.WWW, ".", ":"), arrayOf("", "", "", "/", "/"))
    }

    companion object {
        const val PREMISSION_STRING = "perms[\"{0}\"]"
    }
}
